<template>
  <el-drawer
    :title="formData.name"
    :visible.sync="isOpen"
    :wrapperClosable="false"
    :size="panelWidth"
    append-to-body
    @close="closeHandler">
    <el-scrollbar class="node-detail-content" :class="{ allHeight: !editEnable }" wrap-class="el-scroll-custom-class">
      <div v-if="node.data && directions[node.data.additionalInfo.component]" class="node-directions">
        <div class="directions-cnt">
          <div v-for="item in directions[node.data.additionalInfo.component]" :key="item" class="direc-item">{{ item }}</div>
        </div>
      </div>
      <el-divider v-if="node.data && directions[node.data.additionalInfo.component]"></el-divider>
      <el-form v-if="infoShow" label-position="top" label-width="80px" :rules="rules" :model="formData" ref='infoForm'>
        <el-form-item v-if='!hideName' label="名称" prop="name">
          <el-input v-model="formData.name" maxlength="10" placeholder="请输入" required></el-input>
        </el-form-item>
        <el-form-item label="描述" prop="description">
          <el-input v-model="formData.description" maxlength="100" placeholder="请输入"></el-input>
        </el-form-item>
        <el-divider></el-divider>
      </el-form>
      <component ref="nodeDetail" :is="nodeDetailComponent" :key="componentKey" :initialForm="nodeDetailForm"></component>
    </el-scrollbar>
    <div v-if="editEnable && !hideSaveBtn" class="node-detail-footer">
      <el-button type="primary" @click="getDetailData">保存</el-button>
    </div>
  </el-drawer>
</template>

<script>
import jsUtil from '@/utils/helper/jsUtil'
import { nodeClashValidate } from '@/utils/validate'
import { NodeType } from '@/model/node.model'
import { mapState, mapActions } from 'vuex'
import validateRegex from '@/utils/regex'

export default {
  name: 'NodeDetailsPanel',
  data() {
    const validateName = (rule, value, callback) => {
      if (!value) {
        callback(new Error('名称不能为空'))
      } else {
        if (validateRegex.commonRegex1.test(value)) {
          callback()
        } else {
          callback(new Error('支持中文、数字、字母、短横线（-）、下划线（_）'))
        }
      }
    }
    return {
      isOpen: false,
      infoShow: true,
      hideName: false,
      hideSaveBtn: false,
      directions: {
        ExternalNodeStartNode: ['产品的执行入口。可以设置产品的统一规则。'],
        SceneLibFrontConditionNode: ['前置条件节点：用于定义产品触发的前置条件，只有满足这些条件，产品才会进行触发条件的判断，或者才允许用户主动触发。若前置条件不满足，则产品不会触发。', '前置条件节点非必填项，如果没有定义，则认为该产品不需要前置条件。'],
        SceneLibAutoTouchNode: ['自动触发节点：用于定义一组信号规则，当规则满足时，则产品自动触发，进入产品执行阶段。当规则不满足时，产品不会触发。', '在“阶段编辑模式”中，当自动触发节点满足时，产品同时会开始运行“结束阶段”的逻辑。'],
        SceneLibAutoExitNode: ['自动退出节点：用于定义一组信号规则，当规则满足时，则产品自动退出。当规则不满足时，产品不会自动退出。'],
        SceneLibManualTouchNode: ['主动触发节点：指的是用户通过点击界面按键、或说出语音命令等主动的方式触发产品。', '在“阶段编辑模式”中，当主动触发节点满足时，产品同时会开始运行“结束阶段”的逻辑。'],
        SceneLibManualExitNode: ['主动退出节点：指的是产品进入执行阶段后，用户通过点击界面按键，或说出语音命令的方式退出产品。'],
        InteractiveLibDialogNode: ['弹窗交互节点：指通过车机向用户展示一个弹窗，用于征询用户的反馈，从而决定后续产品的执行逻辑。其支持设置该弹窗的标题、正文、选项文案等内容。'],
        InteractiveLibVoiceNode: ['语音交互节点：指通过语音的方式向征询用户的意见，从而决定后续产品的执行逻辑。支持设置语音的播报内容、选项的文案等内容。'],
        InteractiveLibDialogVoiceNode: ['弹窗+语音交互节点：是指结合弹窗和语音两种方式，同时向用户征询反馈。'],
        AdvancedLibFrequencyNode: ['频率限制节点：使用该节点可控制当前分支的，且位于该节点之后的所有节点的执行频率。通过在不同分支下安放不同的频率限制节点，可以实现更为精细和复杂的频率控制。'],
        AdvancedLibParallelStartNode: ['并行开始节点：当前IDE中的节点默认为串行执行，而通过并行节点，可定义多个分支同时执行。', '并行开始节点必须和并行结束节点成对出现，组成并行执行区域。并行区域内不能嵌套并行节点，并行区域内的节点不能连接到并行区域外，并行区域外的节点不能连接到并行区域内的节点。'],
        AdvancedLibParallelEndNode: ['并行结束节点：与并行开始节点成对出现，组成并行执行区域。并行结束节点支持设置结束条件，例如至少确保一定数量的分支执行完成后才真正结束。', '若并行结束条件满足时，并行区域内未完成的分支依然会执行，但不会执行产品其他逻辑。并行结束节点后的分支恢复为串行执行。'],
        AdvancedLibServeBackNode: ['服务恢复节点：使用该节点可将产品中使用过的服务还原到产品执行前的状态。服务恢复节点支持3种恢复策略：', '1. 不恢复（默认）', '2.智能恢复：产品执行过程中，若该服务状态没有被修改过，则需要恢复；若该服务状态被修改过，则不需要恢复；', '3. 强制恢复：产品执行过程中，不管该服务的状态是否被修改过，都需要恢复'],
        AdvancedLibStateChangeNode: ['状态变化节点：使用该节点可判断具体信号从一个值变化为另一个值的情况。例如：雨刷从开启变为关闭。状态变化节点的输出结果仅支持“成立”，若判断不满足，则产品执行逻辑会一直停留在该节点，直到判断条件满足为止。'],
        AdvancedLibDelayNode: ['延时节点：使用该节点可将当前所处分支暂停一段时间，再继续往后执行。该等待动作仅针对当前分支生效。'],
        AdvancedLibHistoricalNode: ['历史判断节点：产品的历史执行结果数据默认保存在本次上电周期（当监测周期为每天时除外），车辆下电后自动清空相关数据。']
      },
      panelWidth: '30%',
      formData: {
        name: '',
        description: ''
      },
      rules: {
        name: [
          { required: true, validator: validateName, trigger: 'blur' }
        ]
      },

      componentKey: '',
      nodeDetailComponent: '',
      nodeDetailForm: null
    }
  },
  props: {
    node: {
      required: true
    },
    visible: {
      type: Boolean,
      required: true
    }
  },
  computed: {
    ...mapState({
      editEnable: state => true,
      graph: state => state.graph.graph,
      selectedItem: state => state.graph.selectedItem
    })
  },
  watch: {
    visible: {
      handler(val) {
        this.isOpen = val
      },
      immediate: true
    },
    node(val) {
      if (!jsUtil.isVoid(val)) {
        const data = val.getData()
        if (data.type === NodeType.sceneNode) {
          this.panelWidth = '640px'
        } else {
          this.panelWidth = '640px'
        }
        this.formData.name = data.name === '并行节点' ? '并行开始' : data.name // 并行开始节点的title特殊设置一下
        this.formData.description = data.description
        this.nodeDetailComponent = data.additionalInfo.component
        this.nodeDetailForm = data.configuration || data.additionalInfo.defaultConfiguration
        const infoShowList = ['InteractiveLibDialogNode', 'InteractiveLibVoiceNode', 'InteractiveLibDialogVoiceNode', 'ThirdLibThirdNode', 'ServiceLibServiceNode', 'SceneLibSceneNode', 'TspLibTspNode', 'AdvancedLibHistoricalNode', 'SceneLibAutoTouchNode', 'SceneLibAutoExitNode']
        this.infoShow = infoShowList.includes(data.additionalInfo.component)
        const hideNameList = ['InteractiveLibDialogNode', 'InteractiveLibVoiceNode', 'InteractiveLibDialogVoiceNode', 'ThirdLibThirdNode', 'TspLibTspNode', 'AdvancedLibHistoricalNode']
        this.hideName = hideNameList.includes(data.additionalInfo.component)
        const hideSaveList = ['SceneLibManualTouchNode', 'SceneLibManualExitNode', 'AdvancedLibParallelStartNode']
        this.hideSaveBtn = hideSaveList.includes(data.additionalInfo.component)
        this.componentKey = new Date().getTime()
      }
    }
  },
  async mounted() {
    await Promise.all([this.getSignal('vehicleSignal'), this.getSignal('ecoSignal'), this.getService()])
    this.$store.commit('product/save', { name: 'loading', value: false })
  },
  methods: {
    closeHandler() {
      this.$refs.infoForm?.clearValidate()
      this.$emit('close')
    },
    getDetailData() {
      const isValidate = this.$refs.nodeDetail.validateForm()
      let infoValidate = true
      if (this.infoShow) {
        this.$refs.infoForm.validate(valid => {
          infoValidate = !!valid
          return valid
        })
      }
      if (isValidate && infoValidate) {
        const validNode = {
          id: this.selectedItem.data.id,
          data: { name: this.selectedItem.data.data.name, configuration: this.$refs.nodeDetail.getFormData(), additionalInfo: { component: this.selectedItem.data.data.additionalInfo.component } }
        }
        nodeClashValidate([validNode], this.graph)
        this.$emit('save', this.$refs.nodeDetail.getFormData(), jsUtil.deepClone(this.formData))
      }
    },
    ...mapActions('signal', ['getSignal']),
    ...mapActions('service', ['getService'])
  }
}
</script>

<style lang="scss" scoped>
@import "~@/assets/styles/variables.scss";

.node-detail-content {
  padding: 14px 24px 24px;
  height: calc(100% - 64px);
  &.allHeight {
    height: 100%
  }
  & > ::-webkit-scrollbar {
    display: none
  }
}
.node-detail-footer {
  height: 64px;
  line-height: 64px;
  padding: 0 24px;
  border-top: 1px solid $--border-color-base;
  text-align: right;
  overflow: hidden;
}
</style>
